--[[
	Copyright 2012-2013 Alexandru-Mihai Maftei
					aka Vercas

	You may use this for any purpose as long as:
	-	You don't remove this copyright notice.
	-	You don't claim this to be your own.
	-	You properly credit the author (Vercas) if you publish your work based on (and/or using) this.

	If you modify the code for any purpose, the above obligations still apply.

	Instead of copying this code over for sharing, rather use the link:
		https://dl.dropbox.com/u/1217587/GMod/Lua/von%20for%20GMOD.lua

	The author may not be held responsible for any damage or losses directly or indirectly caused by
	the use of vON.

	If you disagree with the above, don't use the code.

-----------------------------------------------------------------------------------------------------------------------------
	
	Thanks to the following people for their contribution:
		-	Divran						Suggested improvements for making the code quicker.
										Suggested an excellent new way of deserializing strings.
										Lead me to finding an extreme flaw in string parsing.
		-	pennerlord					Provided some performance tests to help me improve the code.

-----------------------------------------------------------------------------------------------------------------------------
	
	The value types supported in this release of vON are:
		-	table
		-	number
		-	boolean
		-	string
		-	nil
		-	Entity
		-	Player
		-	Vector
		-	Angle

	These are the native Lua types one would normally serialize.
	+ Some very common GMod Lua types.

-----------------------------------------------------------------------------------------------------------------------------
	
	New in this version:
		-	Fixed errors on vector and angle deserializing.
		-	Added Player datatype.
			It's saved just like an entity, by it's ID, except it's prefixed with "p" instead of "e"
		-	Added errors on (de)serialization when passwing the wrong data type.
		-	Removed two redundant arguments in the serialization functable.
		-	Fixed distribution link pointing to the pure Lua version.
--]]

local _deserialize, _serialize, _d_meta, _s_meta, d_findVariable, s_anyVariable
local sub, gsub, find, insert, concat, error, tonumber, tostring, type, next, getEnt, getPly = string.sub, string.gsub, string.find, table.insert, table.concat, error, tonumber, tostring, type, next, Entity, player.GetByID

function d_findVariable(s, i, len, lastType)
	local i, c, typeRead, val = i or 1
	while true do
		if i > len then
			error("vON: Reached end of string, cannot form proper variable.")
		end
		c = sub(s, i, i)
		if typeRead then
			val, i = _deserialize[lastType](s, i, len)
			return val, i, lastType
		elseif c == "@" then
			return nil, i, lastType
		elseif c == "n" then
			lastType = "number"
			typeRead = true
		elseif c == "b" then
			lastType = "boolean"
			typeRead = true
		elseif c == "\"" then
			lastType = "string"
			typeRead = true
		elseif c == "{" then
			lastType = "table"
			typeRead = true
		elseif c == "e" then
			lastType = "Entity"
			typeRead = true
		elseif c == "p" then
			lastType = "Player"
			typeRead = true
		elseif c == "v" then
			lastType = "Vector"
			typeRead = true
		elseif c == "a" then
			lastType = "Angle"
			typeRead = true
		elseif lastType then
			val, i = _deserialize[lastType](s, i, len)
			return val, i, lastType
		else
			error("vON: Malformed data... Can't find a proper type definition. Char#" .. i .. ":" .. c)
		end
		i = i + 1
	end
end

function s_anyVariable(data, lastType, isNumeric, isKey, isLast, nice, indent)
	if lastType ~= type(data) then
		lastType = type(data)
		return _serialize[lastType](data, true, isNumeric, isKey, isLast, nice, indent), lastType
	end
	return _serialize[lastType](data, false, isNumeric, isKey, isLast, nice, indent), lastType
end

_deserialize = {
	["table"] = function(s, i, len, unnecessaryEnd)
		local ret, numeric, i, c, lastType, val, ind, expectValue, key = {}, true, i or 1
		while true do
			if i > len then
				if unnecessaryEnd then
					return ret, i
				else
					error("vON: Reached end of string, incomplete table definition.")
				end
			end
			c = sub(s,i,i)
			if c == "}" then
				return ret, i
			elseif c == "~" then
				numeric = false
			elseif c == ";" then
			elseif numeric then
				val, i, lastType = d_findVariable(s, i, len, lastType)
				ret[#ret + 1] = val
			else
				if expectValue then
					val, i, lastType = d_findVariable(s, i, len, lastType)
					ret[key] = val
					expectValue, key = false, nil
				elseif c == ":" then
					expectValue = true
				elseif key then
					error("vON: Malformed table... Two keys declared successively? Char#" .. i .. ":" .. c)
				else
					key, i, lastType = d_findVariable(s, i, len, lastType)
				end
			end
			i = i + 1
		end
		return nil, i
	end,
	["number"] = function(s, i, len)
		local i, a = i or 1
		a = find(s, "[;:}~]", i)
		if a then
			return tonumber(sub(s, i, a - 1)), a - 1
		end
		error("vON: Number definition started... Found no end.")
	end,
	["boolean"] = function(s, i, len)
		local c = sub(s,i,i)
		if c == "1" then
			return true, i
		elseif c == "0" then
			return false, i
		end
		error("vON: Invalid value on boolean type... Char#" .. i .. ": " .. c)
	end,
	["string"] = function(s, i, len)
		local res, i, a = "", i or 1
		while true do
			a = find(s, "\"", i, true)
			if a then
				if sub(s, a - 1, a - 1) == "\\" then
					res = res .. sub(s, i, a - 2) .. "\""
					i = a + 1
				else
					return res .. sub(s, i, a - 2), a
				end
			else
				error("vON: String definition started... Found no end.")
			end
		end
	end,
	["Entity"] = function(s, i, len)
		local i, a = i or 1
		a = find(s, "[;:}~]", i)
		if a then
			return getEnt(tonumber(sub(s, i, a - 1))), a - 1
		end
		error("vON: Entity ID definition started... Found no end.")
	end,
	["Player"] = function(s, i, len)
		local i, a = i or 1
		a = find(s, "[;:}~]", i)
		if a then
			return getEnt(tonumber(sub(s, i, a - 1))), a - 1
		end
		error("vON: Player ID definition started... Found no end.")
	end,
	["Vector"] = function(s, i, len)
		local i, a, x, y, z = i or 1
		a = find(s, ",", i)
		if a then
			x = tonumber(sub(s, i, a - 1))
			i = a + 1
		end
		a = find(s, ",", i)
		if a then
			y = tonumber(sub(s, i, a - 1))
			i = a + 1
		end
		a = find(s, "[;:}~]", i)
		if a then
			z = tonumber(sub(s, i, a - 1))
		end
		if x and y and z then
			return Vector(x, y, z), a - 1
		end
		error("vON: Vector definition started... Found no end.")
	end,
	["Angle"] = function(s, i, len)
		local i, a, p, y, r = i or 1
		a = find(s, ",", i)
		if a then
			p = tonumber(sub(s, i, a - 1))
			i = a + 1
		end
		a = find(s, ",", i)
		if a then
			y = tonumber(sub(s, i, a - 1))
			i = a + 1
		end
		a = find(s, "[;:}~]", i)
		if a then
			r = tonumber(sub(s, i, a - 1))
		end
		if p and y and r then
			return Angle(p, y, r), a - 1
		end
		error("vON: Angle definition started... Found no end.")
	end
}

_serialize = {
	["table"] = function(data, mustInitiate, isNumeric, isKey, isLast, first)
		local result, keyvals, len, keyvalsLen, keyvalsProgress, val, lastType, newIndent, indentString = {}, {}, #data, 0, 0
		for k, v in next, data do
			if type(k) ~= "number" or k < 1 or k > len then
				keyvals[#keyvals + 1] = k
			end
		end
		keyvalsLen = #keyvals
		if not first then
			result[#result + 1] = "{"
		end
		if len > 0 then
			for i = 1, len do
				val, lastType = s_anyVariable(data[i], lastType, true, false, i == len and not first, false, 0)
				result[#result + 1] = val
			end
		end
		if keyvalsLen > 0 then
			result[#result + 1] = "~"
			for _i = 1, keyvalsLen do
				keyvalsProgress = keyvalsProgress + 1

				val, lastType = s_anyVariable(keyvals[_i], lastType, false, true, false, false, 0)

				result[#result + 1] = val..":"

				val, lastType = s_anyVariable(data[keyvals[_i]], lastType, false, false, keyvalsProgress == keyvalsLen and not first, false, 0)
				
				result[#result + 1] = val
			end
		end
		if not first then
			result[#result + 1] = "}"
		end
		return concat(result)
	end,
	["number"] = function(data, mustInitiate, isNumeric, isKey, isLast)
		if mustInitiate then
			if isKey or isLast then
				return "n"..data
			else
				return "n"..data..";"
			end
		end

		if isKey or isLast then
			return "n"..data
		else
			return "n"..data..";"
		end
	end,
	["string"] = function(data, mustInitiate, isNumeric, isKey, isLast)
		return "\"" .. gsub(data, "\"", "\\\"") .. "v\""
	end,
	["boolean"] = function(data, mustInitiate, isNumeric, isKey, isLast)
		--	Prefix if we must.
		if mustInitiate then
			if data then
				return "b1"
			else
				return "b0"
			end
		end
		if data then
			return "1"
		else
			return "0"
		end
	end,
	["nil"] = function(data, mustInitiate, isNumeric, isKey, isLast)
		return "@"
	end,
	["Entity"] = function(data, mustInitiate, isNumeric, isKey, isLast)
		data = data:EntIndex()
		if mustInitiate then
			if isKey or isLast then
				return "e"..data
			else
				return "e"..data..";"
			end
		end
		if isKey or isLast then
			return "e"..data
		else
			return "e"..data..";"
		end
	end,
	["Player"] = function(data, mustInitiate, isNumeric, isKey, isLast)
		data = data:EntIndex()

		if mustInitiate then
			if isKey or isLast then
				return "p"..data
			else
				return "p"..data..";"
			end
		end

		if isKey or isLast then
			return "p"..data
		else
			return "p"..data..";"
		end
	end,
	["Vector"] = function(data, mustInitiate, isNumeric, isKey, isLast)
		if mustInitiate then
			if isKey or isLast then
				return "v"..data.x..","..data.y..","..data.z
			else
				return "v"..data.x..","..data.y..","..data.z..";"
			end
		end

		if isKey or isLast then
			return "v"..data.x..","..data.y..","..data.z
		else
			return "v"..data.x..","..data.y..","..data.z..";"
		end
	end,
	["Angle"] = function(data, mustInitiate, isNumeric, isKey, isLast)
		if mustInitiate then
			if isKey or isLast then
				return "a"..data.p..","..data.y..","..data.r
			else
				return "a"..data.p..","..data.y..","..data.r..";"
			end
		end

		if isKey or isLast then
			return "a"..data.p..","..data.y..","..data.r
		else
			return "a"..data.p..","..data.y..","..data.r..";"
		end
	end
}
local _s_table = _serialize.table
local _d_table = _deserialize.table
_d_meta = {
	__call = function(self, str)
		
	end
}
_s_meta = {
	__call = function(self, data)
		
	end
}
von = {}
von.deserialize = setmetatable(_deserialize,_d_meta)
von.serialize = setmetatable(_serialize,_s_meta)

/* register serializer */
GM:RegisterSerializer("von", function(data)
	if type(data) == "table" then
		return _s_table(data, nil, nil, nil, nil, true)
	end
	error("vON: You must serialize a table, not a "..type(data))
end, function(data)
	if type(data) == "string" then
		return _d_table(data, nil, #data, true)
	end
	error("vON: You must deserialize a string, not a "..type(data))
end)